﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ocean.OceanFramework.MVVM;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;
using BusinessEntityObjects;
using BusinessLayer;
using System.Windows;
using System.Windows.Input;
using WPFLOBMVVM.Model;
using System.Windows.Data;
using System.Xml.Linq; 

namespace WPFLOBMVVM.ViewModel
{
public class ApplicationMainWindowViewModel : ClosableViewModelBase 
{ 
    
    #region " Declarations " 
    
    private bool _bolLoadingRecentBlogActivity = false; 
    private ICommand _cmdLoadEditContactsAjaxComboBoxSelectionCommand; 
    private ICommand _cmdLoadEditContactsItemsControlSelectionCommand; 
    private ICommand _cmdRecentBlogActivityCommand; 
    private ICommand _cmdRefreshActivityDataCommand; 
    private Visibility _enumLogoVisibility = Visibility.Visible; 
    private int _intProgress; 
    private List<BlogPost> _objRecentBlogActivity = new List<BlogPost>(); 
    private Dictionary<string, string> _objTimeZoneLookupDictionary; 
    private BackgroundWorker _objBackgroundWorker; 
    private ObservableCollection<ContainerFormViewModelBase> _objForms = new ObservableCollection<ContainerFormViewModelBase>(); 
    
    #endregion 
    
    #region " Properties " 
    
    public ObservableCollection<ContainerFormViewModelBase> Forms { 
        get { return _objForms; } 
    } 
    
    public Visibility LoadingActivityVisibility { 
        get { 
            
            if (_bolLoadingRecentBlogActivity) { 
                return Visibility.Visible; 
            } 
            
            else { 
                return Visibility.Collapsed; 
            } 
        } 
        
    } 
    
    public Visibility LogoVisibility { 
        get { return _enumLogoVisibility; } 
        
        private set { 
            _enumLogoVisibility = value; 
            OnPropertyChanged("LogoVisibility"); 
        } 
    } 
    
    public int Progress { 
        get { return _intProgress; } 
        
        private set { 
            _intProgress = value; 
            OnPropertyChanged("Progress"); 
        } 
    } 
    
    #endregion 
    
    #region " Command Properties " 
    
    public ICommand LoadEditContactsAjaxComboBoxSelectionCommand { 
        get { 
            
            if (_cmdLoadEditContactsAjaxComboBoxSelectionCommand == null) { 
                _cmdLoadEditContactsAjaxComboBoxSelectionCommand = new RelayCommand(this.LoadEditContactsAjaxComboBoxSelectionExecute); 
            } 
            
            return _cmdLoadEditContactsAjaxComboBoxSelectionCommand; 
        } 
    } 
    
    public ICommand LoadEditContactsItemsControlSelectionCommand { 
        get { 
            
            if (_cmdLoadEditContactsItemsControlSelectionCommand == null) { 
                _cmdLoadEditContactsItemsControlSelectionCommand = new RelayCommand(this.LoadEditContactsItemsControlSelectionExecute); 
            } 
            
            return _cmdLoadEditContactsItemsControlSelectionCommand; 
        } 
    } 
    
    public ICommand RecentBlogActivityCommand { 
        get { 
            
            if (_cmdRecentBlogActivityCommand == null) { 
                _cmdRecentBlogActivityCommand = new RelayCommand(RecentBlogActivityExecute, CanRecentBlogActivityExecute); 
            } 
            
            return _cmdRecentBlogActivityCommand; 
        } 
    } 
    
    public ICommand RefreshActivityDataCommand { 
        get { 
            
            if (_cmdRefreshActivityDataCommand == null) { 
                _cmdRefreshActivityDataCommand = new RelayCommand(RefreshActivityDataExecute, CanRefreshActivityDataExecute); 
            } 
            
            return _cmdRefreshActivityDataCommand; 
        } 
    } 
    
    #endregion 
    
    #region " Constructor " 
    
    public ApplicationMainWindowViewModel() 
    {
        _objForms.CollectionChanged += this.Forms_CollectionChanged; 
        _objTimeZoneLookupDictionary = MakeLookUpDictionary(); 
        base.ViewModelFriendlyName = "Simple M-V-VM for WPF LOB"; 
        LoadRecentBlogPosts(); 
    } 
    
    #endregion 
    
    #region " Command Methods " 
    
    public bool CanRecentBlogActivityExecute(object param) 
    { 
        return !_bolLoadingRecentBlogActivity && _objRecentBlogActivity.Count > 0; 
    } 
    
    public bool CanRefreshActivityDataExecute(object param) 
    { 
        return !_bolLoadingRecentBlogActivity; 
    } 
    
    public void LoadEditContactsAjaxComboBoxSelectionExecute(object param) 
    { 
        LoadForm<EditContactsAjaxComboBoxSelectionViewModel>(); 
    } 
    
    public void LoadEditContactsItemsControlSelectionExecute(object param) 
    { 
        LoadForm<EditContactItemsControlSelectionViewModel>(); 
    } 
    
    public void RecentBlogActivityExecute(object param) 
    { 
        
        ContainerFormViewModelBase objForm = this.Forms.FirstOrDefault(( i) => object.ReferenceEquals(typeof(RecentActivityViewModel), i.GetType())); 
        
        if (objForm == null) { 
            LoadViewModel(new RecentActivityViewModel(_objRecentBlogActivity)); 
        } 
        
        else { 
            DisplayFormInTabControl(objForm); 
        } 
        
    } 
    
    public void RefreshActivityDataExecute(object param) 
    { 
        LoadRecentBlogPosts(); 
    } 
    
    private void LoadForm<T>() where T : ContainerFormViewModelBase 
    {


        ContainerFormViewModelBase objForm = this.Forms.FirstOrDefault < ContainerFormViewModelBase>((i) => object.ReferenceEquals(typeof(T), i.GetType())); 
        
        if (objForm == null) { 
            LoadViewModel((ContainerFormViewModelBase)Activator.CreateInstance(typeof(T))); 
        } 
        
        else { 
            
            if (objForm.IsSingleInstanceForm) { 
                DisplayFormInTabControl(objForm); 
            } 
            
            else { 
                LoadViewModel((ContainerFormViewModelBase)Activator.CreateInstance(typeof(T))); 
            } 
        } 
        
        
    } 
    
    #endregion 
    
    #region " Methods " 
    
    public bool CanApplicationClose() 
    { 
        
        foreach (ContainerFormViewModelBase obj in this.Forms) { 
            DisplayFormInTabControl(obj); 
            
            if (obj.VerifyDataBeforeClosing() == false) { 
                return false; 
            } 
        } 
        
        
        return true; 
    } 
    
    private void DisplayFormInTabControl(ContainerFormViewModelBase obj) 
    { 
        
        ICollectionView objCVS = CollectionViewSource.GetDefaultView(_objForms); 
        
        if (objCVS != null) { 
            objCVS.MoveCurrentTo(obj); 
        } 
        
    } 
    
    private void  Forms_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
        
        if (_objForms.Count == 0) { 
            
            if (_enumLogoVisibility != Visibility.Visible) { 
                this.LogoVisibility = Visibility.Visible; 
            } 
        } 
        
        else { 
            
            if (_enumLogoVisibility != Visibility.Collapsed) { 
                this.LogoVisibility = Visibility.Collapsed; 
            } 
        } 
        
        
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) { 
            
            if (e.NewItems != null) { 
                
                foreach (ContainerFormViewModelBase obj in e.NewItems) { 
                    obj.RequestClose += OnChildFormRequestClose; 
                } 
            } 
            
        } 
        
        
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) { 
            
            if (e.OldItems != null) { 
                
                foreach (ContainerFormViewModelBase obj in e.OldItems) { 
                    obj.RequestClose -= OnChildFormRequestClose; 
                } 
            } 
            
        } 
        
        
    } 
    
    private void LoadViewModel(ContainerFormViewModelBase obj) 
    { 
        _objForms.Add(obj); 
        DisplayFormInTabControl(obj); 
    } 
    
    private void OnChildFormRequestClose(object sender, EventArgs e) 
    { 
        
        ContainerFormViewModelBase obj = sender as ContainerFormViewModelBase; 
        
        if (obj != null) { 
            _objForms.Remove(obj); 
            obj = null; 
        } 
        
    } 
    
    #endregion 
    
    #region " Recent Blog Post Methods " 
    
    private void _objBackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
    { 
        
        int intTotalContacts = ContactBLL.CreateInstance().Count(); 
        List<BlogPost> objRecentBlogActivity = new List<BlogPost>(); 
        float intX = 0; 
        
        foreach (ContactFeed obj in ContactBLL.CreateInstance().GetFeeds()) { 
            intX += 1; 
            _objBackgroundWorker.ReportProgress((int)((intX / intTotalContacts) * 100), obj.Author); 
            
            try { 
                
                foreach (BlogPost objBlogPost in RetrieveFeeds(obj.Author, obj.FeedURL)) { 
                    objRecentBlogActivity.Add(objBlogPost); 
                } 
            } 
            
            catch (Exception ex) { 
                ex.Data.Add("ContactFeed", obj); 
                throw ex; 
            } 
        } 
        
        
        _objBackgroundWorker.ReportProgress(100); 
        e.Result = objRecentBlogActivity; 
    } 
    
    private void  _objBackgroundWorker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) 
    { 
        this.Progress = e.ProgressPercentage;
        System.Diagnostics.Debug.WriteLine(e.ProgressPercentage); 
        
        if (e.UserState != null) { 
            base.ViewModelFriendlyName = string.Format("Loading recent blog posts for {0}", e.UserState.ToString()); 
        } 
        
    } 
    
    private void _objBackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) 
    { 
        this.Progress = 100; 
        _bolLoadingRecentBlogActivity = false; 
        base.ViewModelFriendlyName = "Simple M-V-VM for WPF LOB"; 
        OnPropertyChanged("LoadingActivityVisibility"); 
        
        if (e.Error == null) { 
            _objRecentBlogActivity = (List<BlogPost>)e.Result; 
            _objRecentBlogActivity.Sort(); 
        } 
        
        else { 
            
            if (e.Error is System.Net.WebException) { 
                 
                ContactFeed obj = (ContactFeed)e.Error.Data["ContactFeed"]; 
                ViewModelUIService.ExceptionDialog(e.Error, 
                    "Read Feed Exception", "Web Exception While Reading Feed Verify Data", 
                    string.Format("Author:{0}, Feed URL:{1}", obj.Author, obj.FeedURL), 0 ); 
            } 
            
            else { 
                
                if (e.Error.Data.Contains("ContactFeed")) { 
                    
                    ContactFeed obj = (ContactFeed)e.Error.Data["ContactFeed"]; 
                    ViewModelUIService.ExceptionDialog(e.Error, "Exception", "Exception While Loading Recent Blog Posts, Veryify Data", string.Format("Author:{0}, Feed URL:{1}, Exception Message:{2}", obj.Author, obj.FeedURL, e.Error.Message), 0 ); 
                } 
                
                else { 
                    ViewModelUIService.ExceptionDialog(e.Error, "Exception", "Exception While Loading Recent Blog Posts", 0 ); 
                } 
            } 
            
        } 
        
        
    } 
    
    //Special characters in RSS Feeds are automatically cleaned by LINQ-to-Xml but often they 
    //are double encoded, so this method filters feeds again by replacing 
    //some of the special characters with corresponding text. 
    private string EscapeXml(string strDirtyXml) 
    { 
        strDirtyXml = strDirtyXml.Replace("&", "&"); 
        strDirtyXml = strDirtyXml.Replace("<:", "<"); 
        strDirtyXml = strDirtyXml.Replace(">", ">"); 
        //TODO: JAIMER strDirtyXml = strDirtyXml.Replace( @"", "\\"); 
        strDirtyXml = strDirtyXml.Replace("&apos;", "'"); 
        strDirtyXml = strDirtyXml.Replace("ò", "ò"); 
        strDirtyXml = strDirtyXml.Replace("è", "è"); 
        strDirtyXml = strDirtyXml.Replace("ù", "ù"); 
        strDirtyXml = strDirtyXml.Replace("à", "à"); 
        strDirtyXml = strDirtyXml.Replace("…", "..."); 
        return strDirtyXml; 
    } 
    
    private void LoadRecentBlogPosts() 
    { 
        
        if (!_bolLoadingRecentBlogActivity) { 
            _bolLoadingRecentBlogActivity = true; 
            OnPropertyChanged("LoadingActivityVisibility"); 
            _objBackgroundWorker = new BackgroundWorker();
            _objBackgroundWorker.DoWork += this._objBackgroundWorker_DoWork;
            _objBackgroundWorker.ProgressChanged += this._objBackgroundWorker_ProgressChanged;
            _objBackgroundWorker.RunWorkerCompleted += this._objBackgroundWorker_RunWorkerCompleted; 
            _objBackgroundWorker.WorkerReportsProgress = true; 
            _objBackgroundWorker.RunWorkerAsync(); 
        } 
        
    } 
    
    private Dictionary<string, string> MakeLookUpDictionary() 
    { 
        
        //This code is here because http://feeds2.feedburner.com/Pixel-In-Gene uses the old Internet Date Format by including PDT and PST in the date time. 
        Dictionary<string, string> obj = new Dictionary<string, string>(); 
        obj.Add("ACDT", "+1030"); 
        // Australian Central Daylight 
        obj.Add("ACST", "+0930"); 
        // Australian Central Standard 
        obj.Add("ADT", "-0300"); 
        // (US) Atlantic Daylight 
        obj.Add("AEDT", "+1100"); 
        // Australian East Daylight 
        obj.Add("AEST", "+1000"); 
        // Australian East Standard 
        obj.Add("AHDT", "-0900"); 
        obj.Add("AHST", "-1000"); 
        obj.Add("AST", "-0400"); 
        // (US) Atlantic Standard 
        obj.Add("AT", "-0200"); 
        // Azores 
        obj.Add("AWDT", "+0900"); 
        // Australian West Daylight 
        obj.Add("AWST", "+0800"); 
        // Australian West Standard 
        obj.Add("BAT", "+0300"); 
        // Bhagdad 
        obj.Add("BDST", "+0200"); 
        // British Double Summer 
        obj.Add("BET", "-1100"); 
        // Bering Standard 
        obj.Add("BST", "-0300"); 
        // Brazil Standard 
        obj.Add("BT", "+0300"); 
        // Baghdad 
        obj.Add("BZT2", "-0300"); 
        // Brazil Zone 2 
        obj.Add("CADT", "+1030"); 
        // Central Australian Daylight 
        obj.Add("CAST", "+0930"); 
        // Central Australian Standard 
        obj.Add("CAT", "-1000"); 
        // Central Alaska 
        obj.Add("CCT", "+0800"); 
        // China Coast 
        obj.Add("CDT", "-0500"); 
        // (US) Central Daylight 
        obj.Add("CED", "+0200"); 
        // Central European Daylight 
        obj.Add("CET", "+0100"); 
        // Central European 
        obj.Add("CST", "-0600"); 
        // (US) Central Standard 
        obj.Add("EAST", "+1000"); 
        // Eastern Australian Standard 
        obj.Add("EDT", "-0400"); 
        // (US) Eastern Daylight 
        obj.Add("EED", "+0300"); 
        // Eastern European Daylight 
        obj.Add("EET", "+0200"); 
        // Eastern Europe 
        obj.Add("EEST", "+0300"); 
        // Eastern Europe Summer 
        obj.Add("EST", "-0500"); 
        // (US) Eastern Standard 
        obj.Add("FST", "+0200"); 
        // French Summer 
        obj.Add("FWT", "+0100"); 
        // French Winter 
        obj.Add("GMT", "-0000"); 
        // Greenwich Mean 
        obj.Add("GST", "+1000"); 
        // Guam Standard 
        obj.Add("HDT", "-0900"); 
        // Hawaii Daylight 
        obj.Add("HST", "-1000"); 
        // Hawaii Standard 
        obj.Add("IDLE", "+1200"); 
        // Internation Date Line East 
        obj.Add("IDLW", "-1200"); 
        // Internation Date Line West 
        obj.Add("IST", "+0530"); 
        // Indian Standard 
        obj.Add("IT", "+0330"); 
        // Iran 
        obj.Add("JST", "+0900"); 
        // Japan Standard 
        obj.Add("JT", "+0700"); 
        // Java 
        obj.Add("MDT", "-0600"); 
        // (US) Mountain Daylight 
        obj.Add("MED", "+0200"); 
        // Middle European Daylight 
        obj.Add("MET", "+0100"); 
        // Middle European 
        obj.Add("MEST", "+0200"); 
        // Middle European Summer 
        obj.Add("MEWT", "+0100"); 
        // Middle European Winter 
        obj.Add("MST", "-0700"); 
        // (US) Mountain Standard 
        obj.Add("MT", "+0800"); 
        // Moluccas 
        obj.Add("NDT", "-0230"); 
        // Newfoundland Daylight 
        obj.Add("NFT", "-0330"); 
        // Newfoundland 
        obj.Add("NT", "-1100"); 
        // Nome 
        obj.Add("NST", "+0630"); 
        // North Sumatra 
        obj.Add("NZ", "+1100"); 
        // New Zealand 
        obj.Add("NZST", "+1200"); 
        // New Zealand Standard 
        obj.Add("NZDT", "+1300"); 
        // New Zealand Daylight 
        obj.Add("NZT", "+1200"); 
        // New Zealand 
        obj.Add("PDT", "-0700"); 
        // (US) Pacific Daylight 
        obj.Add("PST", "-0800"); 
        // (US) Pacific Standard 
        obj.Add("ROK", "+0900"); 
        // Republic of Korea 
        obj.Add("SAD", "+1000"); 
        // South Australia Daylight 
        obj.Add("SAST", "+0900"); 
        // South Australia Standard 
        obj.Add("SAT", "+0900"); 
        // South Australia Standard 
        obj.Add("SDT", "+1000"); 
        // South Australia Daylight 
        obj.Add("SST", "+0200"); 
        // Swedish Summer 
        obj.Add("SWT", "+0100"); 
        // Swedish Winter 
        obj.Add("USZ3", "+0400"); 
        // USSR Zone 3 
        obj.Add("USZ4", "+0500"); 
        // USSR Zone 4 
        obj.Add("USZ5", "+0600"); 
        // USSR Zone 5 
        obj.Add("USZ6", "+0700"); 
        // USSR Zone 6 
        obj.Add("UT", "-0000"); 
        // Universal Coordinated 
        obj.Add("UTC", "-0000"); 
        // Universal Coordinated 
        obj.Add("UZ10", "+1100"); 
        // USSR Zone 10 
        obj.Add("WAT", "-0100"); 
        // West Africa 
        obj.Add("WET", "-0000"); 
        // West European 
        obj.Add("WST", "+0800"); 
        // West Australian Standard 
        obj.Add("YDT", "-0800"); 
        // Yukon Daylight 
        obj.Add("YST", "-0900"); 
        // Yukon Standard 
        obj.Add("ZP4", "+0400"); 
        // USSR Zone 3 
        obj.Add("ZP5", "+0500"); 
        // USSR Zone 4 
        obj.Add("ZP6", "+0600"); 
        // USSR Zone 5 
        return obj; 
    } 
    
    private DateTime ParseDate(string strDateIn) 
    { 
        
        DateTime datOut = default(DateTime); 
        
        if (System.DateTime.TryParse(strDateIn, out datOut)) { 
            return datOut; 
        } 
        
        string strKey = strDateIn.Substring(strDateIn.LastIndexOf(" ")).Trim(); 
        
        if (_objTimeZoneLookupDictionary.ContainsKey(strKey)) { 
            strDateIn = strDateIn.Replace(strKey, _objTimeZoneLookupDictionary[strKey]); 
            
            if (System.DateTime.TryParse(strDateIn, out datOut)) { 
                return datOut; 
            } 
        } 
        
        
        throw new InvalidCastException("Unable to Parse the following Date: " + strDateIn); 
    } 
    
    private IEnumerable<BlogPost> RetrieveFeeds(string strAuthor, string strFeedURL) 
    { 
        
        DateTime datCutoff = System.DateTime.Today.AddDays(-15);
        try
        {
            XDocument doc = XDocument.Load(strFeedURL);
            var query = (from item in doc.Descendants("item")
                         let
                         datPublished = ParseDate(item.Element("pubDate").Value)
                         orderby datPublished descending
                         where datPublished > datCutoff
                         select new BlogPost
                           {
                               DatePublished = ParseDate(item.Element("pubDate").Value),
                               Title = EscapeXml(item.Element("title").Value),
                               Author = strAuthor,
                               // Category = item.Element("category").Value , 
                               URL = item.Element("link").Value
                           });
            return query; 
        }
        catch (Exception ex)
        {
           //JAIMER TODO? 
        }

        return new List<BlogPost>()  ; 
    }
    #endregion 
} 

}
